﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ColorClusteringSOM.Enumerators;

namespace ColorClusteringSOM
{
    public class ClusterNetwork
    {
        private Network network;
        private int rows;
        private int cols;

        private double lowerTh;
        private double upperTh;

        private NeuronRough[,] clusteredMatrix = null;
        private List<NeuronRough> leaders = null;

        private Helper help = null;

        private RoughFunc roughFunc = null;

        #region Properties
        public List<NeuronRough> Leaders { get { return this.leaders; } }

        public NeuronRough[,] ClusteredMatrix { get { return this.clusteredMatrix; } }

        #endregion

        public ClusterNetwork(Network network)
        {
            this.network = network;
            this.rows = network.Rows;
            this.cols = network.Cols;

            this.clusteredMatrix = new NeuronRough[network.Rows, network.Cols];
            this.leaders = new List<NeuronRough>();

            this.help = new Helper();

            Console.WriteLine("Spusteni clusterizace...");
        }

        public ClusterNetwork(Network network, double lowerTh, double upperTh)
            : this(network)
        {

            this.lowerTh = lowerTh;
            this.upperTh = upperTh;


        }

        /// <summary>
        /// Pro K-means a rough sety
        /// </summary>
        /// <param name="network">sit</param>
        /// <param name="lowerTh">vaha spodni aproximace</param>
        /// <param name="upperTh">vaha horni aproximace</param>
        /// <param name="Threshold"></param>
        public ClusterNetwork(Network network, double threshold, double lowerTh, double upperTh)
            : this(network, lowerTh, upperTh)
        {
            roughFunc = new RoughFunc( threshold,lowerTh,upperTh);
            this.lowerTh = lowerTh;
            this.upperTh = upperTh;

        }

        protected static Random rnd = new Random((int)DateTime.Now.Ticks);
        /// <summary>

        /// </summary>
        /// <param name="k"> pocet clustru</param>
        public void Kmeans(int k)
        {
            List<NeuronRough> clusters = new List<NeuronRough>();
            Neuron neuron = null;

            int counterOfDeadLock = 0;  // nekdy se stane,ze nemuze najit stabilni centra

            //nahodny vyber clusteru ze vstupni mnoziny
            /*
             * Jsou vytvoreni leadri a tem jsou prirazeny prvotni vahy ze vstupu
             */
            //for (int i = 0; i < k; i++)
            //{
            //    neuron = network[rnd.Next(network.Rows - 1), rnd.Next(network.Cols - 1)];
            //    clusters.Add(new NeuronRough(neuron,true, neuron.Weights));
            //}

            bool checkInit = false;
            while (!checkInit)
            {
                clusters = RandomInit(clusters, k);
                checkInit = CheckPossitions(clusters);

            }



            int changeCentroids = 0;
            // pokud se zmenila pozice centroidu, tak pojede prirazovani znovu
            while (changeCentroids < k)
            {
                changeCentroids = 0;
                neuron = null;
                NeuronRough winLeader = null;

                //pruchod siti a a prirazovani prvku ke svym clustrum
                for (int i = 0; i < rows; i++)
                {
                    for (int j = 0; j < cols; j++)
                    {
                        double min = Double.MaxValue;


                        foreach (NeuronRough leader in clusters)
                        {

                            Neuron actualNeuron = network[i, j];
                            // vzdalenost podle vah neuronu
                            double distance = help.EuklidDistanceBetweenNeurons(actualNeuron, leader);
                            if (distance < min)
                            {
                                min = distance;

                                winLeader = leader;
                                neuron = actualNeuron;

                            }

                        }
                        //pridani neuronu do nejblizsimu leadru
                        winLeader.AddToLowerApp(new NeuronRough(neuron, false));


                    }
                }



                // prepocet vah pro cluster leadry
                foreach (NeuronRough leader in clusters)
                {
                    
                    //stare vahy leadra
                    double[] oldWeights = leader.LeaderWeight;

                    //nove vahy leadra
                    double[] newWeights = new double[oldWeights.Length];

                    //pocet neuronu v aproximaci
                    double countNeurons = leader.LowerAppNeuronsArray.Count;

                    //soucet vah vsech prvku v dolni aprox
                    foreach (NeuronRough lowerNeuron in leader.LowerAppNeuronsArray)
                    {
                        for (int i = 0; i < lowerNeuron.Neuron.Weights.Length; i++)
                        {
                            newWeights[i] += lowerNeuron.Neuron.Weights[i];
                        }
                    }

                    if (countNeurons != 0)
                    {

                        
                        for (int i = 0; i < newWeights.Length; i++)
                        {
                            newWeights[i] = newWeights[i]/countNeurons;
                        }

                       // Console.WriteLine("Changing Center's positions OLD=> [{0},{1}] NEW=> [{2},{3}]", xCor, yCor, xCorNEW, yCorNEW);

                        bool flagSAME = true;

                        for (int i = 0; i < newWeights.Length; i++)
                        {
                            if (oldWeights[i] != newWeights[i])
                            {
                                flagSAME = false;
                            }
                        }

                        //pokud se vahy nezmenily
                        if (flagSAME)
                        {
                            //nalezen novy centroid
                            changeCentroids++;

                        }
                        //Console.WriteLine(help.PrintWeights(leader.LeaderWeight,"old"));
                        //Console.WriteLine(help.PrintWeights(newWeights, "new"));
                        leader.LeaderWeight = newWeights;

                    }
                }
                Console.WriteLine("k ={0}, changeCentroids ={1}", k, changeCentroids);
                /*
                 Pokud nebyla nalezana vsechna centra, tak se musi vymazat zaznamy
                 * v dolnich aproximaci vsech leadru, aby se mohly znova priradit
                 */
                if (changeCentroids != k)
                {
                    foreach (NeuronRough leader in clusters)
                    {
                        leader.LowerAppNeuronsArray.Clear();
                    }
                }
                else // byli nalezeny stabilni centra, takze tedka se tento ideal priradi nejakemu neuronu, ktery je nejblize a ktery existuje
                {
                    NeuronRough localN = null;
                  
                    //foreach (NeuronRough leader in clusters)
                    //{
                    //    double min = Double.MaxValue;

                    //    foreach (NeuronRough neuronLower in leader.LowerAppNeuronsArray)
                    //    {
                    //        double distance = help.EuklidDistanceBetweenNeurons2(neuronLower, leader);
                    //        if (distance < min)
                    //        {
                    //            min = distance;

                    //            //winLeader = leader;
                    //            localN = neuronLower;
                                
                    //        }
                            
                    //    }

                    //    Console.WriteLine("old =[{0},{1}]  ---  new =[{2},{3}]", leader.Neuron.X, leader.Neuron.Y, localN.Neuron.X, localN.Neuron.Y);
                       
                    //    leader.Neuron = localN;
                    //    leader.X = localN.X;
                    //    leader.Y = localN.Y;

                    //    Console.WriteLine("old =[{0},{1}]  ---  new =[{2},{3}]\n", leader.Neuron.X, leader.Neuron.Y, localN.Neuron.X, localN.Neuron.Y);
                       

                    //}
                }


            }

            //TODO: kdy byly nalezena vsechna stabilni centra, tak se musi najit reprezentant neboli p
            //presny cluster leader, takze udelat metodu, ktera najde idealniho a damu teda X a Y souradnice

            leaders = clusters;
        }


        private List<NeuronRough> RandomInit(List<NeuronRough> clusters, int k)
        {
            Neuron neuron = null;
            clusters.Clear();


            for (int i = 0; i < k; i++)
            {
                int x = rnd.Next(network.Rows - 1);
                int y = rnd.Next(network.Cols - 1);
                neuron = network[x, y];

                

                clusters.Add(new NeuronRough(neuron, true, neuron.Weights));
            }
            return clusters;
        }

        /// <summary>
        /// Metoda, která zkontroluje, jestli se me
        /// </summary>
        /// <returns></returns>
        private bool CheckPossitions(List<NeuronRough> clusters)
        {
            bool flag = true; // vsechno je ok

            foreach (NeuronRough referen in clusters)
            {

            foreach (NeuronRough neo in clusters)
            {
                if (referen.Equals(neo))
                {
                    continue;
                }

                if (referen.X == neo.X && referen.Y == neo.Y)
                {
                    flag = false;
                    Console.WriteLine("Reset centers possitions!");
                    return false;
                }

            }
            }
            return flag;
        }

        private List<NeuronRough> RandomInitRoughSet(List<NeuronRough> clusters, int k)
        {
            Neuron neuron = null;
            clusters.Clear();
            for (int i = 0; i < k; i++)
            {
                int x = rnd.Next(network.Rows - 1);
                int y = rnd.Next(network.Cols - 1);
                neuron = network[x, y];
                clusters.Add(new NeuronRough(neuron, true, neuron.Weights));
            }
            return clusters;
        }

        int counterFail = 0;

        public void KmeansRoughSet(int k)
        {
            List<NeuronRough> clusters = new List<NeuronRough>();   // shluky i s aproximacemi
            Neuron neuron = null;
           

            bool checkInit = false;
            while (!checkInit)
            {
                clusters = RandomInitRoughSet(clusters, k);
                 checkInit = CheckPossitions(clusters);
    
            }

            bool changeCentr = true;
            bool check = false;
            // pokud se zmenila pozice centroidu, tak pojede prirazovani znovu
            while (changeCentr)
            {
                changeCentr = true;
                neuron = null;
                

                //pruchod siti a a prirazovani prvku ke svym clustrum
                for (int i = 0; i < rows; i++)
                {
                    for (int j = 0; j < cols; j++)
                    {

                        roughFunc.AssigningNeuronToBothAp(network[i, j], clusters);

                    }
                }

                // prepocet center
                //changeCentr = roughFunc.CalculationNewCentroids(clusters);

                changeCentr = roughFunc.CalculationNewCentroids(clusters);

                //pokud true tak je vse vpohode
                //check = CheckPossitions(clusters);
                //if (!check)
                //{
                //    KmeansRoughSet(k);
                //}


                /*
                 Pokud nebyla nalezana vsechna centra, tak se musi vymazat zaznamy
                 * v dolnich aproximaci a hornich vsech leadru, aby se mohly znova priradit
                 */
                if (changeCentr==true)
                {
                    //counterFail++;
                    Console.WriteLine("Possitions of centroids were changed! Centroids' approximations were cleared!");
                    foreach (NeuronRough leader in clusters)
                    {
                        leader.LowerAppNeuronsArray.Clear();
                        leader.UpperAppNeuronsArray.Clear();
                    }
                }
                else // byli nalezeny stabilni centra, takze tedka se tento ideal priradi nejakemu neuronu, ktery je nejblize a ktery existuje
                {
                    NeuronRough localN = null;

                    ////foreach (NeuronRough leader in clusters)
                    ////{
                    ////    double min = Double.MaxValue;

                    ////    foreach (NeuronRough neuronLower in leader.LowerAppNeuronsArray)
                    ////    {
                    ////        double distance = help.EuklidDistanceBetweenNeurons2(neuronLower, leader);
                    ////        if (distance < min)
                    ////        {
                    ////            min = distance;

                    ////            //winLeader = leader;
                    ////            localN = neuronLower;

                    ////        }

                    ////    }

                    ////    leader.Neuron = localN;
                    ////    leader.X = localN.X;
                    ////    leader.Y = localN.Y;

                    ////    // Console.WriteLine("KONECold =[{0},{1}]  ---  new =[{2},{3}]\n", leader.Neuron.X, leader.Neuron.Y, localN.Neuron.X, localN.Neuron.Y);


                    ////}
                }

            }

            leaders = clusters;
        }

        // origos
        //public void KmeansRoughSet(int k)
        //{
        //    List<NeuronRough> clusters = new List<NeuronRough>();   // shluky i s aproximacemi
        //    Neuron neuron = null;


        //    //nahodny vyber clusteru ze vstupni mnoziny
        //    /*
        //     * Jsou vytvoreni leadri a tem jsou prirazeny prvotni vahy ze vstupu
        //     */
        //    for (int i = 0; i < k; i++)
        //    {
        //        neuron = network[rnd.Next(network.Rows - 1), rnd.Next(network.Cols - 1)];
        //        clusters.Add(new NeuronRough(neuron, true, neuron.Weights));
        //    }


        //    for (int i = 0; i < k; i++)
        //    {

        //    }

        //    bool changeCentr = true;
        //    // pokud se zmenila pozice centroidu, tak pojede prirazovani znovu
        //    while (changeCentr)
        //    {
        //        changeCentr = true;
        //        neuron = null;

        //        //pruchod siti a a prirazovani prvku ke svym clustrum
        //        for (int i = 0; i < rows; i++)
        //        {
        //            for (int j = 0; j < cols; j++)
        //            {

        //                roughFunc.AssigningNeuronToBothAp(network[i, j], clusters);

        //            }
        //        }

        //        // prepocet center
        //        //changeCentr = roughFunc.CalculationNewCentroids(clusters);

        //        changeCentr = roughFunc.CalculationNewCentroids(clusters);


        //        /*
        //         Pokud nebyla nalezana vsechna centra, tak se musi vymazat zaznamy
        //         * v dolnich aproximaci a hornich vsech leadru, aby se mohly znova priradit
        //         */
        //        if (changeCentr)
        //        {
        //            Console.WriteLine("Possitions of centroids were changed! Centroids' approximations were cleared!");
        //            foreach (NeuronRough leader in clusters)
        //            {
        //                leader.LowerAppNeuronsArray.Clear();
        //                leader.UpperAppNeuronsArray.Clear();
        //            }
        //        }
        //        else // byli nalezeny stabilni centra, takze tedka se tento ideal priradi nejakemu neuronu, ktery je nejblize a ktery existuje
        //        {
        //            NeuronRough localN = null;

        //            //foreach (NeuronRough leader in clusters)
        //            //{
        //            //    double min = Double.MaxValue;

        //            //    foreach (NeuronRough neuronLower in leader.LowerAppNeuronsArray)
        //            //    {
        //            //        double distance = help.EuklidDistanceBetweenNeurons2(neuronLower, leader);
        //            //        if (distance < min)
        //            //        {
        //            //            min = distance;

        //            //            //winLeader = leader;
        //            //            localN = neuronLower;

        //            //        }

        //            //    }

        //            //    leader.Neuron = localN;
        //            //    leader.X = localN.X;
        //            //    leader.Y = localN.Y;

        //            //    // Console.WriteLine("KONECold =[{0},{1}]  ---  new =[{2},{3}]\n", leader.Neuron.X, leader.Neuron.Y, localN.Neuron.X, localN.Neuron.Y);


        //            //}
        //        }

        //    }

        //    leaders = clusters;
        //}

        public void PrintLeaders()
        {
            foreach (NeuronRough n in leaders)
            {
                Console.WriteLine(n.ToString());
            }
        }

    }
}
